/******************************************************************************
* Copyright (C) 2020, BBI Technology Co.,Ltd All rights reserved.
*
* Author: BBI.
* file iap.c
 
 Note： 本文件可以用于移植,注意如下：
 
 1. 对上层：需要一个dtl645协议的结构体 frame_t;
 2. 对底层：需要修改flash操作的底层接口;
 ******************************************************************************/

#include "iap.h"

uint32_t JumpAddress;
func_ptr_t JumpToApplication;

OTA_pack_t pack;

/**
 *******************************************************************************
 ** \brief  IAP跳转函数
 **
 ** \param  [in] u32Addr                    APP 首地址
 **
 ** \retval Error                           APP 地址错误
 **
 ******************************************************************************/
en_result_t IAP_JumpToApp(uint32_t u32Addr)
{
    uint32_t u32StackTop = *((__IO uint32_t *)u32Addr);  //读取APP程序栈顶地址

    /* 判断栈顶地址有效性 */
    if ((u32StackTop > SRAM_BASE) && (u32StackTop <= (SRAM_BASE + RAM_SIZE)))
    {
        /* 配置跳转到用户程序复位中断入口 */
        JumpAddress = *(__IO uint32_t *)(u32Addr + 4);
        JumpToApplication = (func_ptr_t)JumpAddress;
        /* 初始化用户程序的栈顶指针 */
        __set_MSP(*(__IO uint32_t *)u32Addr);
        JumpToApplication();
    }
    return Error;
}

/**
 *******************************************************************************
 ** \brief  检查BootPara标记区数据值，判断是否需要升级APP程序.
 **
 ** \param  None
 **
 ** \retval None
 **
 ******************************************************************************/
void IAP_UpdateCheck(uint8_t *state)
{ 	
	uint8_t i = 0;
	uint8_t water_arr_buff[7];	
	*state = ready_boot;
	API_Memory_Read_MeterNum(water_arr_buff);
	uint32_t u32AppFlag = *(__IO uint32_t *)BOOT_PARA_ADDRESS; //读出BootLoader para区标记值
    for(i=0;i<6;i++)
	{
	   if(water_arr_buff[i]!=0xff)
		   break;
	}
	if(i>=6)
	{
	    *state = flash_wateraddr_empty;
		return;
	}
	
    if(FLASH_IAP_OPREAT_FLAG == u32AppFlag)  //如果标记值不等于APP_FLAG,表示不需要升级APP程序
    {       
		*state = ready_api;	
		return;		
    }    
	
	if(*state == ready_boot)
	{
	    IAP_JumpToApp(APP_ADDRESS);     //则直接跳转至APP
		return;
	}
}

void IAP_ChangeMode(en_bootloderJump which_jump)
{
	Flash_SectorErase(Sector125_BaseAddr);
	if(which_jump == jump2boot)
	{
		Flash_WriteWord(BOOT_PARA_ADDRESS,FLASH_IAP_OPREAT_FLAG);
	}
}

/**
*******************************************************************************
** \brief	检查备份区是否为空,不为空则擦除.
**
** \param	None
**
** \retval None
**
******************************************************************************/
void IAP_Flash_EmptyCheck(void)
{
	for (int i = 0; i < APP_MAX_SIZE; i+=512) /*30k start, rease 25k,*/
	{
		boolean_t c_ret = BSP_Flash_CheckBlank(BACKUP_START_ADDR + i );
		if(c_ret == FALSE)
		{
			Flash_SectorErase(BACKUP_START_ADDR + i);
		}			
	} 	
}



/**
*******************************************************************************
** \brief	写一个扇区到备份区flash中，此接口调用,且关闭全局中断
		    调用此接口前,备份区已经做了查空检查
            每次写入128字节(OTA升级一包的大小)
**
** \param	None
**
** \retval None
**
******************************************************************************/

void IAP_WriteFirmware2Backup(uint32_t dest_addr, uint8_t *buff)
{
	Flash_WriteBytes(dest_addr, buff, 128);
}


/**
*******************************************************************************
** \brief	把备份区的固件全部还原到APP区域
**
** \param   sectors_num : 要写的扇区总数
**
** \retval None
**
******************************************************************************/
void IAP_WriteFirmware2Flash( uint32_t sectors_num)
{
	uint8_t buff[512] = {0};
	for(int i = 0; i < sectors_num; i++)
	{
		BSP_Flash_ReadBytes(BACKUP_START_ADDR + i*FLASH_SECTOR_SIZE, buff, 512);
		while(Ok != Flash_SectorErase(APP_ADDRESS + i*FLASH_SECTOR_SIZE)); 
		BSP_Flash_Write_Sector(APP_ADDRESS + i*FLASH_SECTOR_SIZE, buff);
		//printf("\r\nWrite flash [%x] successful",APP_ADDRESS+i);
		//LPUart_SendData(M0P_LPUART0, i);//发送数据
	}
}


/*****
	printf("\r\nMCU OTA update successful!!!!");
	printf("\r\nJump to App now!!!");	
****/

#if 0


void IAP_Ack_Start_Ready(void)
{
	
	uint8_t ack_buff[16] = {0xfe,0xfe,0x68,0x99,0x99,0x99,0x99,0x99,0x99,0x68,\
							0x33,0x02,0x03,0x00,0x00,0x16};
							
	memcpy(ack_buff+3,meter.MeterNum.num,DL645_NUMADDR_SIZE);

	ack_buff[14] = protocol_cal_crc8(ack_buff + 2,12);	
	memcpy(payload_f.send_buff,ack_buff,16);
	API_LocalCOM_Write(COM1,payload_f.send_buff,16,ENABLE);	

}


void IAP_Ack_End_OK(bool packlost_status)
{
	uint8_t ack_buff[25] = {0xfe,0xfe,0x68,0x99,0x99,0x99,0x99,0x99,0x99,0x68,\
							0x33,0x02,/*Len*/0x01,0x00,0xFF,0x16};

	uint8_t LogbuffLen = 0; /*"FF****FF"*/
	uint8_t crc8 = 0;
	uint8_t AllTotalbuffLen = 0;

	memcpy(ack_buff+3,meter.MeterNum.num,DL645_NUMADDR_SIZE);
																									
	if(pack.total_num %8 == 0)  /*exp : 70 packs is Not fill all Bytes*/
	{
		LogbuffLen = pack.total_num/8; 
	}
	else
	{
		LogbuffLen = pack.total_num/8 + 1; 
	}

	ack_buff[11] = LogbuffLen + 3;  /*协议Len*/

	ack_buff[13] =  pack.total_num ;
	ack_buff[14] =  pack.total_num >>8;

	for(int i = 0; i < LogbuffLen; i++)
	{
		ack_buff[15 + i] = 0xff;
	}

	AllTotalbuffLen = 17 + LogbuffLen;

	crc8 = protocol_cal_crc8(ack_buff + 2, AllTotalbuffLen -4);

	ack_buff[15 + LogbuffLen] =  crc8;
	
	ack_buff[16 + LogbuffLen] = 0x16;

	memcpy(payload_f.send_buff,ack_buff,AllTotalbuffLen);
	API_LocalCOM_Write(COM1,payload_f.send_buff,AllTotalbuffLen,ENABLE);

}

void IAP_Ack_MD5Status(iap_ack_type status)
{

	uint8_t ack_buff[16] = {0xfe,0xfe,0x68,0x99,0x99,0x99,0x99,0x99,0x99,0x68,\
							0x33,0x02,0x02,0x00,0x00,0x16};
							
	memcpy(ack_buff+3,meter.MeterNum.num,DL645_NUMADDR_SIZE);

	if(status == Ack_md5_Error)
	{
		ack_buff[13] = 0x01;
	}
	//ack_buff[14] = protocol_cal_crc8(ack_buff + 2,12);	
	memcpy(payload_f.send_buff,ack_buff,16);
	API_LocalCOM_Write(COM1,payload_f.send_buff,16,ENABLE);		
}

/**
 ***********************************************************************
 ** \brief 对固件进行md5校验，校验成功 返回true;
 **  前2字节前导符
 **
 ** \param 1 :void
 ** 
 ** \retval  true: 校验成功
 ** 
 ***********************************************************************/
bool IAP_firmware_MD5_Checkout(uint8_t *md5_InputValue)
{	
	uint8_t  md5_out[16] = {0};
	MD5_CTX md5;
	MD5Init(&md5);       //初始化用于md5加密的结构 
	
	uint8_t *inbuff = (uint8_t *)(BACKUP_START_ADDR); 
	 
	MD5Update(&md5,inbuff,pack.total_num*128);    //对欲加密的字符进行加密

	MD5Final(&md5,md5_out);  //获得最终结果
	
	// 如果memcmp返回值 = 0，则表示 str1等于str2
	if(	memcmp(md5_out, md5_InputValue,16) == 0)
	{
		return true;
	}
	return false;	
}

#endif

